var dhack =
{
	/** Configuration information. */
	config:
	{
		showNewbieHelp: true
	},
	/** Game title. */
	title: "Metro City Panic!",
	/** Program version. */
	version: "0.2",
	/** The message log mode. */
	logWindow: null,
	/** The status display. */
	statusWindow: null,
	/** The player object. */
	player: null,
	/** The current map object. */
	map: null,
	/** The stack of mode handlers. */
	_modeStack: [],
	/** Keeps track of what mode we're in. */
	_gameMode: "none",
	/** Program entry point.
	 */
	main: function()
	{
		host.init(
			80,
			25,
			function(action, chr)
			{
				dhack.handleAction(action, chr);
			}
		);
		host.atExit(function(){dhack._atExit();});
		
		host.screen.setCell(3, 3, Char.DBAR_NESW, Color.white, null);
		host.screen.setCell(3, 2, Char.DBAR_NS, Color.white, null);
		host.screen.setCell(3, 4, Char.DBAR_NS, Color.white, null);
		host.screen.setCell(2, 3, Char.DBAR_EW, Color.white, null);
		host.screen.setCell(4, 3, Char.DBAR_EW, Color.white, null);
		//return;
		
		this.loadConfig();

		this.enterTitleMode();
		return;

		if(!this.loadGame())
			this.newGame();
		this.logWindow.pushMessage("DHack Version " + this.version + " by Norman B. Lancaster (QBRADQ)");

		// DEBUG
		this.textBox("General Help");
	},
	/** Called when the application exits. We use this to prevent save scumming.
	 */
	_atExit: function()
	{
		if(this._gameMode === "game")
		{
			this.saveGame();
		}
		this.saveConfig();
	},
	/** Called every time a turn has ended.
	 */
	onTurnEnded: function()
	{
		if(this.statusWindow)
			this.statusWindow.render(true);
	},
	/** Enters the main game mode.
	 *@param message The message to display upon entering game mode.
	 */
	enterGameMode: function(message)
	{
		this._modeStack = [];
		this.logWindow = new MessageLogMode(host.screen, 0, 20, 60, 5, 128);
		this.addModeHandler(this.logWindow);
		this.logWindow.pushMessage(message);
		this.statusWindow = new StatusMode(host.screen, 60, 0, 20, host.screen.height);
		this.addModeHandler(this.statusWindow);
		this.addModeHandler(new MainMenuMode(host.screen));
		this.addModeHandler(new MapMode(host.screen, this.map));
		if(this.config.showNewbieHelp)
		{
			this.config.showNewbieHelp = false;
			this.textBox("General Help");
		}
		this._gameMode = "game";
	},
	/** Enters the title screen mode.
	 */
	enterTitleMode: function()
	{
		this._modeStack = [];
		this.logWindow = null;
		this.statusWindow = null;
		this.addModeHandler(new TitleMode(host.screen));
		this._gameMode = "title";
	},
	/** Handle an input action.
	 *@param action A string describing the action.
	 *@param chr The character string that prompted the action (optional).
	 */
	handleAction: function(action, chr)
	{
		for(var i = this._modeStack.length - 1; i >= 0; --i)
		{
			var mode = this._modeStack[i];
			if(mode.handleAction)
			{
				if(mode.handleAction(action, chr))
					break;
			}
		}
	},
	/** Add a mode handler to the stack of mode handlers.
	 *@param handler The handler to add.
	 */
	addModeHandler: function(handler)
	{
		if(this._modeStack.indexOf(handler) >= 0)
			return;
		this._modeStack.push(handler);
		this.render();
	},
	/** Remove a mode handler from the stack.
	 *@param handler The handler to remove.
	 */
	removeModeHandler: function(handler)
	{
		var idx = this._modeStack.indexOf(handler);
		if(idx < 0)
			return;
		this._modeStack.splice(idx, 1);
		this.render();
	},
	/** Re-render all mode handlers.
	 */
	render: function()
	{
		for(var i = 0; i < this._modeStack.length; ++i)
		{
			var mode = this._modeStack[i];
			if(mode.render)
				mode.render();
		}
	},
	/** Save configuration settings.
	 */
	saveConfig: function()
	{
		if(!host.isSaveAvailable())
			return;

		var saveData;

		try
		{
			saveData = ron.serialize(this.config, null);
			host.saveData("config", saveData);
		}
		catch(exp) { }
	},
	/** Load configuration settings.
	 */
	loadConfig: function()
	{
		if(!host.isSaveAvailable())
			return;

		var saveData = null;

		try
		{
			saveData = host.loadData("config", null);
			if(!saveData)
				return;
			var config = ron.deserialize(saveData, null);
			for(var o in config)
			{
				if(!config.hasOwnProperty(o))
					continue;
				this.config[o] = config[o];
			}
		}
		catch(exp) { }
	},
	/** Save a game file.
	 */
	saveGame: function()
	{
		// See if saving works
		if(!host.isSaveAvailable())
		{
			this.logWindow.pushMessage("%90Saving is not supported.");
			return false;
		}

		var saveData;

		try
		{
			// Build the save data
			var save =
			{
				player: this.player,
				map: this.map
			}
			saveData = ron.serialize(save, this.version);
			host.saveData("save", saveData);
		}
		catch(exp)
		{
			dhack.reportCriticalError("Saving game failed: " + exp);
			return false;
		}

		this.logWindow.pushMessage("%a0Saved game successfully (" + util.bytesToKb(saveData.length) + "kb).");
		return true;
	},
	/** Load a game file.
	 */
	loadGame: function()
	{
		// See if saving works
		if(!host.isSaveAvailable())
		{
			this.reportCriticalError("Saving is not supported.");
			return false;
		}

		var saveData = null;

		try
		{
			saveData = host.loadData("save");
			if(!saveData)
				return false;
			var save = ron.deserialize(saveData, this.version);
			if(save === null)
			{
				this.reportCriticalError("The saved game present is for version " + save.version + " and is not compatible.");
				return false;
			}

			// Load proceedure
			this.player = save.player;
			this.map = save.map;
			this.map.addPlayer(this.player, true);
		}
		catch(exp)
		{
			this.reportCriticalError("Loading game failed: " + exp);
			return false;
		}

		this.enterGameMode("%a0Game loaded successfully (" + util.bytesToKb(saveData.length) + "kb).");
		return true;
	},
	/** Start a new game.
	 */
	newGame: function()
	{
		this.deleteSaveFile();
		this.player = new PlayerMobile();
		/*
		this.player.addEntity(Item.newById("club"));
		this.player.addEntity(Item.newById("dagger"));
		this.player.addEntity(Item.newById("leather-shield"));
		this.player.addEntity(Item.newById("sling"));
		this.player.addEntity(Item.newById("cloths"));
		this.player.addEntity(Item.newById("potion heal"));
		this.player.addEntity(Item.newById("potion heal"));
		this.player.addEntity(Item.newById("potion heal"));
		this.player.addEntity(Item.newById("potion speed"));
		this.player.addEntity(Item.newById("potion speed"));
		this.player.addEntity(Item.newById("potion speed"));
		this.player.addEntity(Item.newById("potion mana"));
		this.player.addEntity(Item.newById("potion mana"));
		this.player.addEntity(Item.newById("potion mana"));
		this.player.addEntity(Item.newById("scroll fireball"));
		this.player.addEntity(Item.newById("scroll fireball"));
		this.player.addEntity(Item.newById("scroll fireball"));
		this.player.skills.push(Skill.newById("haste"));
		this.player.skills.push(Skill.newById("fireball"));
		this.player.skills.push(Skill.newById("berzerk"));
		*/
		this.map = new Map(host.screen, 0, 0, 60, 20, 1);
		//this.map.generate();
		this.map.fromStatic(staticLevels["beta1"]);
		this.map.addPlayer(this.player);

		this.enterGameMode("%a0Started new game.");
		return true;
	},
	/** Delete the player's game file.
	 */
	deleteSaveFile: function()
	{
		try { host.deleteData("save"); }
		catch(exp) {}
	},
	/** Determine if a saved game is available.
	 */
	isSaveAvailable: function()
	{
		// See if saving works
		if(!host.isSaveAvailable())
			return false;
		return host.isValueAvailable("save");
	},
	/** Quickly pull up a standard text box.
	 *@param name Name of the text resource to display in the text box.
	 */
	textBox: function(name)
	{
		this.addModeHandler(
			new TextReaderMode(
				host.screen,
				"%f0" + text[name],
				null,
				null
			)
		);
	},
	/** Utility method for displaying a message box.
	 *@param text The text to display. Do not include newlines.
	 */
	messageBox: function(text)
	{
		this.addModeHandler(
			new MenuListMode(
				host.screen,
				50,
				text,
				null,
				null
			)
		);
	},
	/** Standard critical error reporting method.
	 *@param error A description of the error.
	 */
	reportCriticalError: function(error)
	{
		this.messageBox("%90" + error);
	}
};
